////////////////////////////////////////////////////////////////////////////////
// File: 		EEColorManager.java
// Version: 	0.6
//
// Copyright © 2017 Entertainment Experience, LLC
////////////////////////////////////////////////////////////////////////////////

package com.rockchips.mediacenter.utils;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;

import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.view.Surface;

public class EEColorManager
{
	public enum Result
	{
		ERROR_ALREADY_INITIALIZED,
		ERROR_NOT_INITIALIZED,

		ERROR_ACTIVITY_NULL,
		ERROR_SURFACE_NULL,

		ERROR_EECOLOR_TABLE_NOT_FOUND,
		ERROR_HDR_TABLE_NOT_FOUND,

		SUCCESS
	};

	public static Result initialize(Activity activity, Surface surface, boolean eeColorEnabled, String eeColorTable, boolean hdrEnabled, String hdrTable)
	{
		if (mSurface != null)
		{
			return Result.ERROR_ALREADY_INITIALIZED;
		}

		if (activity == null)
		{
			return Result.ERROR_ACTIVITY_NULL;
		}

		if (surface == null)
		{
			return Result.ERROR_SURFACE_NULL;
		}

		mActivity = activity;
		mSurface = surface;

		Result result = setEEColorTable(eeColorTable);
		if (result != Result.SUCCESS)
		{
			return result;
		}

		result = setHDRTable(hdrTable);
		if (result != Result.SUCCESS)
		{
			return result;
		}

		enableEEColor(eeColorEnabled);
		enableHDR(hdrEnabled);

		return result;
	}

	public static Result uninitialize(Surface surface)
	{
		if (mSurface == null)
		{
			return Result.ERROR_NOT_INITIALIZED;
		}

		mSurface = null;

		return Result.SUCCESS;
	}

	public static Result enableEEColor(boolean enabled)
	{
		if (mSurface == null)
		{
			return Result.ERROR_NOT_INITIALIZED;
		}

		mEEColorEnabled = enabled;
		updateTableType();

		return Result.SUCCESS;
	}

	public static boolean isEEColorEnabled()
	{
		return mEEColorEnabled;
	}

	public static Result enableHDR(boolean enabled)
	{
		if (mSurface == null)
		{
			return Result.ERROR_NOT_INITIALIZED;
		}

		mHDREnabled = enabled;
		updateTableType();

		return Result.SUCCESS;
	}

	public static boolean isHDREnabled()
	{
		return mHDREnabled;
	}

	public static Result setEEColorTable(String tableName)
	{
		if (mSurface == null)
		{
			return Result.ERROR_NOT_INITIALIZED;
		}

		if (tableName == null)
		{
			tableName = DEFAULT_TABLE_NAME_SD;
		}

		Result result = setTable(TABLE_INDEX_SD, tableName);
		if (result != Result.SUCCESS)
		{
			return result;
		}

		mEEColorTable = tableName;
		return Result.SUCCESS;
	}

	public static String getEEColorTable()
	{
		return mEEColorTable;
	}

	public static Result setHDRTable(String tableName)
	{
		if (mSurface == null)
		{
			return Result.ERROR_NOT_INITIALIZED;
		}

		if (tableName == null)
		{
			tableName = DEFAULT_TABLE_NAME_HDR;
		}

		Result result = setTable(TABLE_INDEX_HDR, tableName);
		if (result != Result.SUCCESS)
		{
			if(result == Result.ERROR_EECOLOR_TABLE_NOT_FOUND)
			{
				result = Result.ERROR_HDR_TABLE_NOT_FOUND;
			}
			
			return result;
		}

		mHDRTable = tableName;
		return Result.SUCCESS;
	}

	public static String getHDRTable()
	{
		return mHDRTable;
	}

	private enum TableType
	{
		SD,
		HDR,
		HDR_SD
	};

	private static final int TABLE_INDEX_SD = 0;
	private static final int TABLE_INDEX_HDR = 1;

	private static final int SURFACE_FLINGER_TRANSACT_EECOLOR_ENABLED = 10000;
	private static final int SURFACE_FLINGER_TRANSACT_SEND_BYTES = 10001;
	private static final int SURFACE_FLINGER_TRANSACT_TABLE_TYPE = 10002;

	private static final String DEFAULT_TABLE_NAME_SD = "SD_MEDIUM_ROOM_LIGHTING_NORMAL_SKINUINT32SF";
	private static final String DEFAULT_TABLE_NAME_HDR = "REC2020ToREC709Knee250Gamma850FullLevelUINT32SF";

	private static Surface mSurface = null;
	private static Activity mActivity = null;

	private static boolean mEEColorEnabled = false;
	private static boolean mHDREnabled = false;
	private static String mEEColorTable = null;
	private static String mHDRTable = null;

	private static void updateTableType()
	{
		if (mEEColorEnabled || mHDREnabled)
		{
			setEEColorEnabled(true);
			setTableType(mEEColorEnabled && mHDREnabled ? TableType.HDR_SD : (mHDREnabled ? TableType.HDR : TableType.SD));
		}
		else
		{
			setEEColorEnabled(false);
		}
	}

	private static Result setTable(int tableIndex, String tableName)
	{
		AssetManager assetManager = mActivity.getResources().getAssets();
		try
		{
			String[] packageAssets = assetManager.list("tables");
			for (String packageAsset : packageAssets)
			{
				if (!packageAsset.substring(0, packageAsset.length() - 4).equals(tableName))
				{
					continue;
				}
				
				int[] data = readFile(packageAsset);
				if (data == null)
				{
					return Result.ERROR_EECOLOR_TABLE_NOT_FOUND;
				}

				sendBytes(tableIndex, data);
				mEEColorTable = tableName;

				return Result.SUCCESS;
			}
		}
		catch (Exception exception)
		{
			return Result.ERROR_EECOLOR_TABLE_NOT_FOUND;
		}

		return Result.ERROR_EECOLOR_TABLE_NOT_FOUND;
	}

	private static int[] readFile(String packageAsset)
	{
		AssetManager assetManager = mActivity.getResources().getAssets();
		try
		{
			File cacheFile = new File(mActivity.getCacheDir(), "file.tmp");
			OutputStream outputStream = new FileOutputStream(cacheFile);
			copyFile(new BufferedInputStream(assetManager.open("tables/" + packageAsset)), outputStream);
			outputStream.flush();
			outputStream.close();

			RandomAccessFile randomAccessFile = new RandomAccessFile(cacheFile, "r");
			long bytes = randomAccessFile.length();
			randomAccessFile.close();

			int[] intData = new int[(int)bytes];
			DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(cacheFile)));
			for (int i = 0; i < (int)bytes; ++i)
			{
				intData[i] = dataInputStream.readByte() & 0xff;
			}
			dataInputStream.close();
			return intData;
		}
		catch (Exception exception)
		{
			return null;
		}
	}

	private static void copyFile(InputStream inputStream, OutputStream outputStream) throws IOException
	{
		byte[] buffer = new byte[16 * 1024];
		int readBytes = inputStream.read(buffer);
		while (readBytes != -1)
		{
			outputStream.write(buffer, 0, readBytes);
			readBytes = inputStream.read(buffer);
		}
	}

	private static void setEEColorEnabled(boolean enabled)
	{
		try
		{
			IBinder surfaceFlingerBinder = ServiceManager.getService("SurfaceFlinger");

			if (surfaceFlingerBinder != null)
			{
				Parcel data = Parcel.obtain();
				data.writeInterfaceToken("android.ui.ISurfaceComposer");
				data.writeInt(enabled ? 1 : 0);
				surfaceFlingerBinder.transact(SURFACE_FLINGER_TRANSACT_EECOLOR_ENABLED, data, null, 0);
				data.recycle();
			}
		}
		catch (RemoteException remoteException)
		{
		}
	}

	private static void setTableType(TableType tableType)
	{
		try
		{
			IBinder surfaceFlingerBinder = ServiceManager.getService("SurfaceFlinger");
			if (surfaceFlingerBinder != null)
			{
				Parcel data = Parcel.obtain();
				data.writeInterfaceToken("android.ui.ISurfaceComposer");
				data.writeInt(tableType.ordinal());
				surfaceFlingerBinder.transact(SURFACE_FLINGER_TRANSACT_TABLE_TYPE, data, null, 0);
				data.recycle();
			}
		}
		catch (RemoteException remoteException)
		{
		}
	}

	private static void sendBytes(int index, int[] bytes)
	{
		try
		{
			IBinder surfaceFlingerBinder = ServiceManager.getService("SurfaceFlinger");
			if (surfaceFlingerBinder != null)
			{
				Parcel data = Parcel.obtain();
				data.writeInterfaceToken("android.ui.ISurfaceComposer");
				data.writeInt(index);
				data.writeInt(bytes.length);
				data.writeIntArray(bytes);
				surfaceFlingerBinder.transact(SURFACE_FLINGER_TRANSACT_SEND_BYTES, data, null, 0);
				data.recycle();
			}
		}
		catch (RemoteException remoteException)
		{
		}
	}
}
